#!/usr/bin/env python

from twisted.internet.protocol import *
from twisted.internet import reactor
import struct, time, core

#################################################

nodes = core.nodes

class ClientPlaceHolder:
    def __init__(self):
        self.buf = ''

    def push(self, data):
        self.buf += data

class MesonProtocol(Protocol):
    def connectionMade(self):
        if isinstance(self.factory, MesonClientFactory):
            peeraddr = self.transport.getPeer()
            self.peer = core.addr_to_ident((peeraddr.host, peeraddr.port))
            self.push(struct.pack('H', port))
            self.push(nodes[self.peer].buf)
            nodes[self.peer] = self
            self.stage = 1
            self.pending = 2
        else:
            self.peer = None
            self.stage = 0
            self.pending = 2
        self.buf = ''
    
    def dataReceived(self, data):
        print time.time(), ' RCVD:', repr(data)
        self.buf += data
        while len(self.buf) >= self.pending:
            if self.stage == 1:
                self.stage = 2
                self.pending = struct.unpack('H', self.buf[: 2])[0]
                self.buf = self.buf[2:]
            elif self.stage == 2:
                self.stage = 1
                on_message(self.buf[: self.pending], self.peer)
                self.buf = self.buf[self.pending:]
                self.pending = 2
            elif self.stage == 0:
                self.stage = 1
                peeraddr = self.transport.getPeer()
                peerlisten = struct.unpack('H', self.buf[: 2])[0]
                self.peer = core.addr_to_ident((peeraddr.host, peerlisten))
                nodes[self.peer] = self
                self.buf = self.buf[2:]
                self.pending = 2
            reactor.iterate()

    def connectionLost(self, reason = connectionDone):
        try:
            del nodes[self.peer]
        except KeyError:
            pass
        self.transport.loseConnection()

    def push(self, data):
        print time.time(), 'SEND:', repr(data)
        self.transport.write(data)


class MesonServerFactory(ServerFactory):
    protocol = MesonProtocol


class MesonClientFactory(ClientFactory):
    protocol = MesonProtocol


def connect(ident):
    try:
        return nodes[ident]
    except KeyError:
        if ident.startswith('#'):
            return None
        nodes[ident] = ClientPlaceHolder()
        addr = core.ident_to_addr(ident)
        reactor.connectTCP(addr[0], addr[1], client_fact)
        return nodes[ident]


def on_timer(ident):
    on_message(None, ident)

def timer(id, timeout):
    reactor.callLater(timeout, on_timer, (id + '@timer',))


def send(msg, ident):
    ch = connect(ident)
    if ch:
        ch.push(struct.pack('H', len(msg)) + msg)


def init(hook, addr, backlog):
    global on_message, port, server_fact, client_fact
    on_message = hook
    if addr:
        port = addr[1]
        server_fact = MesonServerFactory()
        reactor.listenTCP(port, server_fact, backlog, addr[0])
    else:
        port = 0
    client_fact = MesonClientFactory()



def run():
    reactor.run()
